//-------------------------------------------------------------------------------------
// <copyright file="Connect.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation. All rights reserved.
//      THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
//      EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
//      WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// </copyright>
// <author>Greg Beech</author>
// <email>gregbee@microsoft.com</email>
// <date>2004-04-20</date>
// <summary>
//      Connects to a share using the supplied credentials.
// </summary>  
//-------------------------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks.Folder.Share
{
    #region Using directives

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Management;
    using System.Text;

    using Microsoft.Build.Framework;
    using Microsoft.Build.Utilities;

    #endregion

    #region Class Comments
    /// <summary>
    /// Connects to a share using the supplied credentials. You can use the
    /// <see cref="Microsoft.Sdc.Tasks.Folder.Share.Disconnect"/> task to disconnect from the
    /// share.
    /// </summary>
    /// <remarks>
    /// <code><![CDATA[<Share.Connect ShareName="shareName" Machine="machine" UserName="userName" Password="password" LocalName="localName" ResourceType="resourceType" Connections="connections" />]]></code>
    /// <para>where:</para>
    /// <para><i>shareName (Required)</i></para>
    /// <para>
    /// The name of the share to connect to.
    /// </para>
    /// <para><i>machine (Required)</i></para>
    /// <para>
    /// The name or IP address of the machine the share is on.
    /// </para>
    /// <para><i>userName (Required)</i></para>
    /// <para>
    /// The name of the user to connect to the share as, in the form DOMAIN\Username.
    /// </para>
    /// <para><i>password</i></para>
    /// <para>
    /// The password for the user account specified by <i>userName</i>. The default is no password.
    /// </para>
    /// <para><i>localName</i></para>
    /// <para>
    /// The name of the local device to connect the share to, e.g. E: or LPT1:. This cannot be specified
    /// if the <i>resourceType</i> is "Any". The default is no local name.
    /// </para>
    /// <para><i>resourceType</i></para>
    /// <para>
    /// The type of resource being connected to, which can be one of the following values: Any, Disk, Printer, 
    /// Ipc.
    /// </para>
    /// <para><i>connections</i></para>
    /// <para>
    /// The number of connections to make to the share.
    /// </para>
    /// <seealso cref="Microsoft.Sdc.Tasks.Folder.Share.Create"/>
    /// <seealso cref="Microsoft.Sdc.Tasks.Folder.Share.DeleteShare"/>
    /// <seealso cref="Microsoft.Sdc.Tasks.Folder.Share.Disconnect"/>
    /// <seealso cref="Microsoft.Sdc.Tasks.Folder.Share.Exists"/>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test">
    ///         <Share.Connect 
    ///             ShareName="MyShare"
    ///             Machine="MyMachine" 
    ///             UserName="domain\username" 
    ///             Password="password1" 
    ///             LocalName="E:" 
    ///             ResourceType="Disk" 
    ///             Connections="1" />
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>
    #endregion
    public class Connect : ShareTaskBase
    {
        #region Member Variables

        private int connections = 1;
        private string localName;
        private DeviceType resourceType = DeviceType.Disk;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new task to connect to a share.
        /// </summary>
        public Connect()
        {
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the number of connections that are established with the resource.
        /// </summary>
        /// <value>
        /// The number of connections that are established with the resource.
        /// </value>
        public int Connections
        {
            get
            {
                return this.connections;
            }

            set
            {
                this.connections = value;
            }
        }

        /// <summary>
        /// Gets or sets the local device name that the share is mapped to, e.g. E: or LPT1:. This can be null.
        /// </summary>
        /// <value>
        /// The local device name that the share is mapped to.
        /// </value>
        public string LocalName
        {
            get
            {
                return this.localName;
            }

            set
            {
                this.localName = value;
            }
        }

        /// <summary>
        /// Gets or sets the type of resource being connected to. The default is "Disk". Note that you cannot
        /// set the local name if "Any" is used.
        /// </summary>
        /// <value>
        /// One of the following values: "Any", "Disk", "Printer", "Ipc".
        /// </value>
        public string ResourceType
        {
            get
            {
                return this.resourceType.ToString();
            }

            set
            {
                this.resourceType = (DeviceType)Enum.Parse(typeof(DeviceType), value);
            }
        }

        /// <summary>
        /// Gets or sets the user name used to connect to the share. This should be in the format DOMAIN\Username.
        /// </summary>
        /// <value>
        /// The user name used to connect to the share.
        /// </value>
        [Required]
        public override string UserName
        {
            get
            {
                return base.UserName;
            }

            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value", "You must provide a user name to connect to the share with.");
                }

                if (!value.Contains(@"\"))
                {
                    throw new FormatException(@"The user name must be in the format DOMAIN\Username.");
                }
                base.UserName = value;
            }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Executes the task
        /// </summary>
        protected override void InternalExecute()
        {
            //create a USE_INFO_2 structure for the share
            string[] userNameParts = this.UserName.Split('\\');
            NativeMethods.USE_INFO_2 useInfo = new NativeMethods.USE_INFO_2();
            useInfo.ui2_local = this.localName;
            useInfo.ui2_remote = this.GetSharePath();
            useInfo.ui2_password = this.Password;
            useInfo.ui2_asg_type = (uint)this.resourceType;
            useInfo.ui2_usecount = (uint)this.connections;
            useInfo.ui2_username = userNameParts[1];
            useInfo.ui2_domainname = userNameParts[0];
            
            //use the resource
            uint paramErrorIndex;
            uint returnCode = NativeMethods.NetUseAdd(null, 2, ref useInfo, out paramErrorIndex);
            if (returnCode != NativeMethods.NERR_Success)
            {
                Win32Exception windowsException = new Win32Exception((int)returnCode);
                if (returnCode == NativeMethods.ERROR_INVALID_PARAMETER)
                {
                    string errorText = string.Format(
                        CultureInfo.InvariantCulture, 
                        "Failed to connect as the parameter \"{0}\" has invalid value \"{1}\".",
                        this.GetUseInfoInvalidParameterFriendlyName(paramErrorIndex),
                        this.GetUseInfoInvalidParameterValue(useInfo, paramErrorIndex));
                    throw new ApplicationException(errorText, windowsException);
                }
                else
                {
                    throw windowsException;
                }
            }
        }

        #endregion
    }
}